package com.jweb.framework.service.sqlmap.impl;

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;

import javax.sql.DataSource;

import net.sf.ehcache.Cache;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.ObjectUtils;


import com.jweb.framework.core.service.sqlmap.SqlMap;
import com.jweb.framework.service.sqlmap.support.MyBatisExceptionTranslator;
import com.jweb.framework.service.sqlmap.support.SpringManagedTransactionFactory;

/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：数据库相关文件的加载
 */
public class SqlMapFactoryBean implements FactoryBean<SqlMap> , InitializingBean{
	
	private DataSource dataSource ;
	private Resource configLocation ;
	private Resource[] mappingLocations ;
	private Properties properties ;
	private String environment = SqlMapFactoryBean.class.getName();
	
	private String encoding = "UTF-8";
	private SqlSessionFactory sqlSessionFactory ;
	private TransactionFactory transactionFactory ;
	private SqlMap sqlMap ;
	
	// 加入Ehcache
	private Cache cache ;
	
	private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
	private PersistenceExceptionTranslator persistenceExceptionTranslator ;
	
	public SqlSessionFactory getSqlSessionFactory(){
		return this.sqlSessionFactory ;
	}
	
	public void setConfigLocation(Resource configLocation){
		this.configLocation = configLocation ;
	}
	
	public void setMappingLocations(Resource[] mappingLocations){
		this.mappingLocations = mappingLocations ;
	}
	
	public void setProperties(Properties properties){
		this.properties = properties ;
	}
	
	public void setEnvironment(String environment){
		this.environment = environment ;
	}
	
	public void setEncoding(String encoding){
		this.encoding = encoding; 
	}
	
	public void setDataSource(DataSource dataSource){
		this.dataSource = dataSource ;
	}
	
	public void setTransactionFactory(TransactionFactory transactionFactory){
		this.transactionFactory = transactionFactory ;
	}
	
	public void setCache(Cache cache) {
		this.cache = cache;
	}
	
	public void afterPropertiesSet() throws Exception {
		this.sqlMap = buildSqlMap(this.configLocation , this.mappingLocations , this.properties , this.environment);
	}
	
	protected SqlMap buildSqlMap(Resource configLocation ,Resource[] mappingLocations , Properties properties , String environment) throws IOException {
		
		if(configLocation == null){
			throw new IllegalArgumentException("'configLocation' is required");
		}
		XMLConfigBuilder xmlConfigBuilder ;
		Configuration configuration ;
		try {
			InputStreamReader inputStreamReader = new InputStreamReader(configLocation.getInputStream() , this.encoding);
			xmlConfigBuilder = new XMLConfigBuilder(inputStreamReader , null ,properties);
			configuration = xmlConfigBuilder.parse();
			
		} catch (Exception e) {
			throw new NestedIOException("Failed to parse config resource: " + configLocation, e);
		} finally {
			ErrorContext.instance().reset();
		}
		
		if(this.transactionFactory == null){
			this.transactionFactory = (TransactionFactory) new SpringManagedTransactionFactory(this.dataSource);
		}
		
		Environment localEnvironment = new Environment(this.environment , this.transactionFactory , this.dataSource);
		configuration.setEnvironment(localEnvironment);
		
		if(!ObjectUtils.isEmpty(mappingLocations)){
			for(Resource resource : mappingLocations){
				if(resource == null){
					break ;
				}
				String str ;
				if(resource instanceof ClassPathResource){
					str = ((ClassPathResource)resource).getPath();
				}
				else {
					str = resource.toString();
				}
				
				try {
					InputStreamReader inputStreamReader2 = new InputStreamReader(resource.getInputStream() , this.encoding);
					XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(inputStreamReader2 , configuration , str , configuration.getSqlFragments());
					xmlMapperBuilder.parse();
					
				} catch (Exception e) {
					throw new NestedIOException("Failed to parse mapping resource : " + resource + "'" , e);
				} finally {
					ErrorContext.instance().reset();
				}
			}
		}
		
		this.sqlSessionFactory = this.sqlSessionFactoryBuilder.build(configuration);
		if(this.persistenceExceptionTranslator == null){
			this.persistenceExceptionTranslator = new MyBatisExceptionTranslator(this.sqlSessionFactory.getConfiguration().getEnvironment().getDataSource() , true);
		}
		
		//return new SqlMapImpl(this.dataSource , this.sqlSessionFactory , this.persistenceExceptionTranslator);
		return new SqlMapImpl(this.dataSource , this.sqlSessionFactory , this.persistenceExceptionTranslator , this.cache);
	}
	
	
	
	public SqlMap getObject(){
		return this.sqlMap ;
	}
	
	public Class<? extends SqlMap> getObjectType(){
		return (this.sqlMap != null) ? this.sqlMap.getClass() : SqlMap.class ;
	}
	
	public boolean isSingleton(){
		return true ;
	}
	
	
}
